﻿
using Microsoft.AspNetCore.Identity;
using Leo.Cloud.IdentityLogin.Dtos;
using Leo.Cloud.IdentityLogin.Servcies;
using Leo.Core.Application;
using Leo.Core.Jwt;
using System;
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Identity;
using Volo.Abp.Identity.Settings;
using Volo.Abp.ObjectMapping;
using Volo.Abp.Security.Claims;
using Volo.Abp.Settings;
using static Volo.Abp.Identity.Settings.IdentitySettingNames;

namespace Leo.Cloud.IdentityLogin.Services
{
    public class PassportService : BaseApplicationService, IPassportService
    {
        private readonly IdentityUserStore _identityUserStore;
        private readonly IdentityUserManager _identityUserManager;
        private readonly ITokenService _jwtTokenService;
        private readonly ILookupNormalizer _lookupNormalizer;

        public PassportService(IdentityUserStore identityUserStore, 
            IdentityUserManager identityUserManager,
            ITokenService jwtTokenService,
            ILookupNormalizer lookupNormalizer)
        {
            _identityUserStore = identityUserStore;
            _identityUserManager = identityUserManager;
            _jwtTokenService = jwtTokenService;
            _lookupNormalizer = lookupNormalizer;
        }

        public async Task<IdentityLoginOutput> SignIn(IdentityLoginInput input)
        {
            CheckBussionErrors(input.UserName.IsNullOrEmpty(), "请输入账号名");
            CheckBussionErrors(input.Password.IsNullOrEmpty(), "请输入密码");
            var normalizedUserName= _lookupNormalizer.NormalizeName(input.UserName);
            var identityUser = await _identityUserStore.FindByNameAsync(normalizedUserName);
            CheckBussionErrors(identityUser == null, "用户名无效");
            CheckBussionErrors(!identityUser.IsActive, "此用户已被禁用");
            CheckBussionErrors(!identityUser.LockoutEnabled, "此用户登录次数过多,已被锁定!");
            var ispass = await _identityUserManager.CheckPasswordAsync(identityUser, input.Password);
            //await TraceFailedAccessAttempts(identityUser, ispass);
            CheckBussionErrors(!ispass, "登录密码错误!");
            //await ResetAccessFailed(identityUser);
            var accountProfile = ObjectMapper.Map<Volo.Abp.Identity.IdentityUser, AccountProfile>(identityUser);
            List<Claim> claims = await BuildClaims(identityUser);
            var jwtToken = _jwtTokenService.GenerateAccessToken(claims.ToArray());
            await TraceClaims(identityUser, claims);
            return new IdentityLoginOutput
            {
                AccountProfile = accountProfile,
                Token = jwtToken
            };
        
        }

        private async Task TraceClaims(
            Volo.Abp.Identity.IdentityUser identityUser,
            List<Claim> claims,
            string loginProvider="", 
            string tokenName="", 
            string value = "")
        {
            await _identityUserStore.RemoveClaimsAsync(identityUser, claims);
            await _identityUserStore.AddClaimsAsync(identityUser, claims);
            await _identityUserStore.UpdateAsync(identityUser);
            //await _identityUserManager.SetAuthenticationTokenAsync(identityUser, "", "", "");
        }
        private async Task<List<Claim>> BuildClaims(Volo.Abp.Identity.IdentityUser identityUser)
        {
            var claims = new List<Claim>();

            claims.Add(new Claim(AbpClaimTypes.UserName, identityUser.Name));
            claims.Add(new Claim(AbpClaimTypes.UserId, identityUser.Id.ToString()));
            claims.Add(new Claim(AbpClaimTypes.Email, identityUser.Email ?? string.Empty));
            claims.Add(new Claim(AbpClaimTypes.PhoneNumber, identityUser.PhoneNumber ?? string.Empty));
            claims.Add(new Claim(AbpClaimTypes.TenantId, identityUser.TenantId.HasValue ? identityUser.TenantId.ToString() : String.Empty));
            var roleNames = await _identityUserStore.GetRolesAsync(identityUser);
            foreach (var roleName in roleNames)
            {
                claims.Add(new Claim(AbpClaimTypes.Role, roleName));
            }

            return claims;
        }

        private async Task ResetAccessFailed(Volo.Abp.Identity.IdentityUser identityUser)
        {
            await _identityUserManager.ResetAccessFailedCountAsync(identityUser);
            await _identityUserStore.UpdateAsync(identityUser);
        }

        private async Task TraceFailedAccessAttempts(Volo.Abp.Identity.IdentityUser identityUser, bool ispass)
        {
            if (!ispass)
            {
                var maxFailedAccessAttempts = await SettingProvider.GetAsync<int>(Lockout.MaxFailedAccessAttempts);
                if (identityUser.AccessFailedCount == maxFailedAccessAttempts)
                {
                    await _identityUserStore.SetLockoutEnabledAsync(identityUser, true);
                }
                await _identityUserStore.IncrementAccessFailedCountAsync(identityUser);
                await _identityUserStore.UpdateAsync(identityUser);
            }
        }
    }
}
